/**********************************************************************
 Copyright (C) 2010 Teppo Eeva

 This file is part of Arithmetic Game.
 For more information, see <http://code.google.com/p/arithmetic-game/>

 Arithmetic Game is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 Arithmetic Game is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with Arithmetic Game.  If not, see <http://www.gnu.org/licenses/>.
 **********************************************************************/

#include "arithengine.h"
#include <QTimer>
#include <QDateTime>
#include <QDebug>


const int KBonusInterval = 20;
const int KPenaltyMSecs = 2000;
const int KBonusFactor = 10;


ArithEngine::ArithEngine(QObject *parent) :
    QObject(parent),
    m_state(Idle),
    m_score( 0 ),
    m_correctCounter( 0 ),
    m_incorrectCounter( 0 ),
    m_level( 0 ),
    m_timerInterval( 20 ),
    m_diffLevel( Hard )
{
    m_timer = new QTimer(this);
    connect(m_timer, SIGNAL(timeout()), this, SLOT(handleTimerEvent()));
}


ArithEngine::~ArithEngine()
{
}

int ArithEngine::maxTime()
{
    qDebug() << "maxTime: "<< startLevelTime.msecsTo(endLevelTime);
    return startLevelTime.msecsTo(endLevelTime);
}


int ArithEngine::score()
{
    return m_score;
}

int ArithEngine::level()
{
    return m_level;
}

void ArithEngine::startGame()
{
    if (m_state == Idle)
    {
        if (m_level == 0) // Start game
        {
            qsrand(QDateTime::currentDateTime().toTime_t());

            m_level = 1;
            m_score = 0;
        }
        else // Start next level
        {
            qDebug() << "onStartGame - Level " << m_level << " started";

        }
        m_question.randomize(m_level);
        emit newQuestionAvailable();

        startLevelTime.start();
        nowTime = QTime::currentTime();

        switch (m_diffLevel)
        {
        case Easy:
            endLevelTime = startLevelTime.addSecs( KEasyLevelSecs );
            m_levelFactor = 3;
            break;
        case Moderate:
            endLevelTime = startLevelTime.addSecs( KModerateLevelSecs );
            m_levelFactor = 2;
            break;
        case Hard:
            endLevelTime = startLevelTime.addSecs( KHardLevelSecs );
            m_levelFactor = 1;
        default:
            break;
        }

        m_bonusFactor = KBonusFactor;
        m_correctCounter = 0;
        m_incorrectCounter = 0;
        m_state = Playing;

        m_timer->start( m_timerInterval );
    }
    else
    {
        qDebug() << "onStartGame - Error: game already running";

    }

}



void ArithEngine::endGame()
{
    m_timer->stop();
    emit gameOver( m_score, m_level );
    m_level = 0;
    m_correctCounter = 0;
    m_state = Idle;
    emit elapsedTimeChanged( 0 );
}

void ArithEngine::setDiffLevel(ArithDiffLevel diffLevel)
{
    m_diffLevel = diffLevel;
}

void ArithEngine::handleAnswer( int answer )
{
    qDebug("handleAnswer - Time elapsed: %d ms", startLevelTime.elapsed());
    qDebug() << "handleAnswer - Answer: " << answer << "Correct: " << m_question.answer();

    if ( answer == m_question.answer() )
    {
        m_score++;
        emit scoreChanged(m_score);

        m_correctCounter++;
        emit correctAnswerSubmitted();
        if ( m_correctCounter >= 10 )
        {
            m_state = Bonus;
            nowTime = QTime::currentTime().addMSecs( m_incorrectCounter * KPenaltyMSecs );
            emit levelCompleted( m_level );
        }
        else
        {
            // Generate new question. Shall we emit questions object as argument??
            m_question.randomize(m_level);
            emit newQuestionAvailable();
        }
    }
    else
    {
        m_incorrectCounter++;
        emit incorrectAnswerSubmitted( m_question.answer() ); // Correct answer can be passed as argument

        nowTime = QTime::currentTime().addMSecs( m_incorrectCounter * KPenaltyMSecs );
        emit elapsedTimeChanged( startLevelTime.msecsTo(nowTime) );

        if  (nowTime < endLevelTime)
        {
            // Generate new question. Shall we emit questions object as argument??
            m_question.randomize(m_level);
            emit newQuestionAvailable();
        }
        else
        {
            endGame();
        }

    }
}

ArithQuestion ArithEngine::question()
{
    return m_question;
}

void ArithEngine::handleTimerEvent()
{
    switch( m_state )
    {
        case Idle:
        {
            // Should not ever happen
            break;
        }

        case Playing:
        {
            if  (nowTime.msecsTo( endLevelTime ) > 0)
            {
                nowTime = QTime::currentTime().addMSecs( m_incorrectCounter * KPenaltyMSecs );
                emit elapsedTimeChanged( startLevelTime.msecsTo(nowTime) );
            }
            else
            {
                endGame();
            }

            break;
        }

        case Bonus:
        {
            if  (nowTime.msecsTo( endLevelTime ) > 0)
            {
                nowTime = nowTime.addMSecs( m_levelFactor * 100 );
                m_bonusFactor--;
                if (m_bonusFactor<=0)
                {
                    m_score++; // Bonus points is rewarded of remaining time
                    emit scoreChanged(m_score);
                    m_bonusFactor = KBonusFactor;
                }
            }
            else
            {
                m_timer->stop();
                m_state = Idle;
                m_level++;
                emit readyToStartNextLevel( m_level );
            }
            emit elapsedTimeChanged(startLevelTime.msecsTo( nowTime ));
            break;
        }
    }

}
